package cn.edu.cuit.elena.transaction.controller;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import cn.edu.cuit.elena.trace.Tracer;

public class TaskMonitor
    implements Runnable
{
    /**
     * Evaluate whether there is any more task or not.
     * */
    private volatile boolean noTask = true;

    /**
     * Evaluate whether TaskMonitor started or not.
     * */
    private volatile boolean started = false;

    /**
     * All tasks be monitoring are putting in this map.
     * */
    private Map<String, IdentifiedRunnable> monitoringTask;

    private Map<String, IdentifiedRunnable> historicalTask;

    private static TaskMonitor taskMonitor;

    static
    {
        taskMonitor = new TaskMonitor();
    }

    public static TaskMonitor initilize()
    {
        return taskMonitor;
    }

    public void start()
    {
        if( started )
        {
            return;
        }
        Thread monitor = new Thread( this );
        monitor.setName( "Task Monitor" );
        monitor.start();
    }

    private TaskMonitor()
    {
        monitoringTask = new HashMap<String, IdentifiedRunnable>();
        historicalTask = new HashMap<String, IdentifiedRunnable>();
    }

    /**
     * Add a Runnable object into monitoring list, due to starting a thread is not as soon as method
     * stack invocation invoke,so monitor thread will <strong>blocking</strong> until task started
     * and call its <strong>notifyAll()</strong> method or <strong>time out</strong>.
     * */
    public void monitoringTask( IdentifiedRunnable task )
    {
        //noTask was initialized to TRUE,
        //once this method was called, notask must be FALSE.
        try
        {
            //Due to starting a thread is not as soon as method stack invocation invoke,
            //so monitor thread will blocking until task started and call its notifyAll() 
            //method or 1 second time out. 
            //Waiting to task running.
            synchronized( this )
            {
                wait( 1000 );
            }
        }
        catch( InterruptedException e )
        {
            e.printStackTrace();
        }
        if( noTask )
        {
            noTask = false;
        }
        synchronized( monitoringTask )
        {
            monitoringTask.put( task.getIdentifier(), task );
        }

        Tracer.debugTrace( TaskMonitor.class, "Start to moniot task : " + task.getIdentifier(), null );
    }

    public void run()
    {
        Tracer.debugTrace( TaskMonitor.class, "Launch Task Monitor ...", null );

        while( true )
        {
            try
            {
                if( noTask )
                {
                    continue;
                }

                Iterator<Entry<String, IdentifiedRunnable>> iterator = monitoringTask.entrySet().iterator();
                if( iterator == null )
                {
                    continue;
                }

                synchronized( iterator )
                {
                    while( iterator.hasNext() )
                    {
                        Entry<String, IdentifiedRunnable> entry = iterator.next();
                        IdentifiedRunnable task = entry.getValue();
                        String key = entry.getKey();
                        synchronized( task )
                        {
                            if( task.isCompleted() )
                            {
                                //                                synchronized( historicalTask )
                                //                                {
                                historicalTask.put( key, task );
                                //                                }
                                monitoringTask.remove( key );
                                Tracer.debugTrace( TaskMonitor.class, "remove task : " + task.getIdentifier(), null );
                            }
                            else
                            {
                                //TODO
                                //If task is alive, what to do next ?            
                                //Set progress!!!!!!!!!!.
                            }
                        }
                    }

                    //If there is no more task to monitoring, set noTask to TRUE
                    if( monitoringTask.size() == 0 )
                    {
                        noTask = true;
                    }
                }
            }
            catch( Exception e )
            {
                Tracer.fatalTrace( TaskMonitor.class,
                    "Run() method Exception ignored!!!, continue monitoring." + e.getMessage(), e );
            }
        }
    }

    /**
     * Obtain progression of the specified task.
     * 
     * @param taskName
     *            IdentifiedRunnableImp#getIdentifier()
     * */
    public int getTaskProgresson( String taskName )
    {
        synchronized( monitoringTask )
        {
            IdentifiedRunnable task = monitoringTask.get( taskName );
            return task.getProgression();
        }
    }

    /**
     * @return All output lines from specified running task.
     * */
    public String[] getTaskOutput( String taskName )
    {
        //TODO

        IdentifiedRunnable task;
        synchronized( monitoringTask )
        {
            task = monitoringTask.get( taskName );
        }

        if( task == null )
        {
            return null;
        }

        return task.getOutput();
    }

    public IdentifiedRunnable getTaskById( String taskId )
    {
        synchronized( monitoringTask )
        {
            return monitoringTask.get( taskId );
        }
    }

    public IdentifiedRunnable getRetiredTaskById( String taskId )
    {
        synchronized( historicalTask )
        {
            return historicalTask.get( taskId );
        }
    }

    public Map<String, IdentifiedRunnable> getTasks()
    {
        Map<String, IdentifiedRunnable> map = new HashMap<String, IdentifiedRunnable>();
        synchronized( monitoringTask )
        {
            map.putAll( monitoringTask );
        }
        return map;
    }

    public Map<String, IdentifiedRunnable> getRetiredTasks()
    {
        Map<String, IdentifiedRunnable> map = new HashMap<String, IdentifiedRunnable>();
        synchronized( historicalTask )
        {
            map.putAll( historicalTask );
        }
        return map;
    }
}
